feat(web): set up tRPC v11 API layer with React Query v5#41
Conversation
Closes #36 - Install @trpc/server, @trpc/client, @trpc/react-query, @tanstack/react-query, zod - Scaffold server/trpc.ts: TRPCContext, publicProcedure, protectedProcedure (Bearer/cookie auth), createCallerFactory - Add routers: health (proxies Go backend), auth (session/signOut stubs), notifications (FCM token stub) - Wire /api/trpc/[trpc] fetchRequestHandler (GET + POST) - Add lib/trpc/client.tsx: TRPCProvider wrapping QueryClientProvider - Add lib/trpc/server.ts: cached createServerCaller for Server Components - Wire TRPCProvider via app/providers.tsx into root layout - Add Vitest tests for health router and protectedProcedure (UNAUTHORIZED, Bearer, cookie) - Pin postgres:16 in docker-compose.yml - Update docs: styling.md and components.md (shadcn/ui), data-fetching.md (tRPC section), new trpc.md
|
Warning Review limit reached
More reviews will be available in 44 minutes and 13 seconds. Learn how PR review limits work. Your organization has run out of usage credits. Purchase more credits in the billing tab to continue. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits. 🚦 How do rate limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (9)
📝 WalkthroughWalkthroughAdds a full tRPC v11 + React Query v5 API layer to the Next.js frontend. This includes tRPC server initialization, ChangestRPC API Layer
Postgres Version Pin
Sequence DiagramsequenceDiagram
participant Browser
participant TRPCProvider
participant NextjsAPIRoute
participant appRouter
participant GoBackend
rect rgba(99, 102, 241, 0.5)
note over Browser, TRPCProvider: Client Component path
Browser->>TRPCProvider: trpc.health.useQuery()
TRPCProvider->>NextjsAPIRoute: POST /api/trpc/health.query (httpBatchLink)
NextjsAPIRoute->>appRouter: fetchRequestHandler → health.query()
appRouter->>GoBackend: fetch BACKEND_URL/health
GoBackend-->>appRouter: { status, database }
appRouter-->>NextjsAPIRoute: JSON result
NextjsAPIRoute-->>TRPCProvider: response
TRPCProvider-->>Browser: React Query state (data/isLoading/isError)
end
rect rgba(16, 185, 129, 0.5)
note over Browser, appRouter: Server Component path
Browser->>appRouter: createServerCaller() → caller.health.query()
appRouter->>GoBackend: fetch BACKEND_URL/health
GoBackend-->>appRouter: { status, database }
appRouter-->>Browser: typed result (no HTTP round-trip)
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related issues
Suggested labels
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@backend/docker-compose.yml`:
- Line 3: Update the Postgres version in the testcontainer setup and
documentation to match the pinned version in docker-compose.yml. Locate the
testcontainer configuration in health_repository_test.go and the testing
documentation, and change any references from postgres:latest to postgres:16.
This ensures consistency across docker-compose.yml, the testcontainer setup, and
the documentation to prevent environment drift and avoid hiding version-specific
database behavior during testing.
In `@web/lib/trpc/client.tsx`:
- Around line 11-34: Create a Vitest test file for web/lib/trpc/client.tsx to
add unit test coverage for the new getBaseUrl function and TRPCProvider
component. For getBaseUrl, write tests to verify the correct base URL is
returned in different environments: when running in the browser (returns empty
string), when VERCEL_URL environment variable is set (returns https URL), and
when running locally without VERCEL_URL (returns localhost). For TRPCProvider,
write a test to verify it renders children correctly and sets up the TRPC and
QueryClient providers properly.
In `@web/lib/trpc/server.ts`:
- Around line 9-17: The `createServerCaller` function lacks required Vitest unit
tests. Create a new test file (typically `server.test.ts` or `server.spec.ts`)
in `web/lib/trpc/` and write tests that validate header propagation from the
request object into the context, verify that the TRPC caller is correctly
instantiated with the created context, and test edge cases such as missing or
empty headers. Ensure the tests use Vitest's standard patterns and cover the
main code path of `createServerCaller` to confirm both header handling and
context/caller creation work as expected.
In `@web/server/routers/__tests__/health.test.ts`:
- Around line 6-8: The global fetch stub created with vi.stubGlobal('fetch',
mockFetch) is not being restored after the tests run, which causes the mock to
persist and affect other tests. Add an afterEach or afterAll hook that calls
vi.unstubAllGlobals() or explicitly restores the fetch function to ensure the
stub is cleaned up between tests and does not leak into other test suites.
In `@web/server/routers/health.ts`:
- Around line 7-8: The fetch call to `${BACKEND_URL}/health` in the health check
handler lacks a timeout mechanism, which can cause the request to hang
indefinitely if the backend is unresponsive. Add a timeout by using an
AbortController: create an AbortController instance, set a setTimeout to abort
the controller after a reasonable duration (such as 5-10 seconds), and pass the
controller's signal in the fetch options. This ensures the request will be
terminated automatically if the backend fails to respond within the timeout
period.
In `@web/server/trpc.ts`:
- Around line 23-27: The hasBearer check in protectedProcedure only validates
that the authHeader starts with 'Bearer ' but does not verify that an actual
token exists after the prefix, allowing empty tokens to pass. Similarly, the
hasSessionCookie check uses a simple substring match that does not validate an
actual session value is present. Strengthen both checks by: for hasBearer,
verify that there is a non-empty token string following the 'Bearer ' prefix
(not just the prefix itself), and for hasSessionCookie, properly parse the
cookie to confirm that __session has an assigned value rather than just checking
for the substring '__session=' in the cookie header.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 2f4def12-9880-4120-b23f-a905cbe95966
⛔ Files ignored due to path filters (1)
web/pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (23)
backend/docker-compose.ymlweb/AGENTS.mdweb/app/api/trpc/[trpc]/route.tsweb/app/layout.tsxweb/app/page.tsxweb/app/providers.tsxweb/components/home/about.tsxweb/components/home/hero.tsxweb/docs/_index.mdweb/docs/components.mdweb/docs/data-fetching.mdweb/docs/styling.mdweb/docs/trpc.mdweb/lib/trpc/client.tsxweb/lib/trpc/server.tsweb/package.jsonweb/server/routers/__tests__/auth.test.tsweb/server/routers/__tests__/health.test.tsweb/server/routers/_app.tsweb/server/routers/auth.tsweb/server/routers/health.tsweb/server/routers/notifications.tsweb/server/trpc.ts
- protectedProcedure: reject empty Bearer tokens and match __session cookie by exact key (prevents substring-in-value spoofing) - health router: add 5 s AbortSignal timeout to backend fetch - health.test.ts: unstub global fetch in afterAll to prevent leakage; update fetch assertion to include options argument - auth.test.ts: add edge-case tests for empty Bearer, empty cookie value, and __session= appearing inside another cookie's value - Extract getBaseUrl to lib/trpc/utils.ts; add @vitest-environment node unit tests covering browser, Vercel, and localhost branches - Add lib/trpc/__tests__/server.test.ts: unit tests for createServerCaller with mocked next/headers and react.cache - backend: pin testcontainers postgres image to postgres:16
Closes #36
Summary
publicProcedureproxies to the Go backend;protectedProcedureenforces auth via Bearer token or__sessioncookieTRPCProvider/QueryClientProviderwired into the root layout viaapp/providers.tsxcreateServerCaller) available for use in Server Componentspostgres:16inbackend/docker-compose.ymlTest plan
pnpm test— 51 tests pass (includes health router and protectedProcedure unit tests)pnpm lint— 0 errorspnpm build— clean TypeScript + static generation,/api/trpc/[trpc]route is dynamicSummary by CodeRabbit
New Features
Documentation
Chores